import asyncio
import json
import logging
import socket
import serial
import websockets
import threading
import yaml

# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 创建文件处理器
file_handler = logging.FileHandler('barcode_reader.log')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))

# 创建日志记录器
logger = logging.getLogger('oneGunScan')
logger.addHandler(file_handler)

class BarcodeReader:
    def __init__(self, config):
        self.websocket = None
        self.count = 0
        self.codes = []
        self.serialPort = config['serial']['port']
        self.serialBaudRate = config['serial']['baudRate']
        self.serialTimeout = config['serial']['timeout']
        self.server_ip = config['TCPClient']['host']
        self.server_port = config['TCPClient']['port']
        self.openMessage = config['message']['open']
        self.closeMessage = config['message']['close']
        self.closeMessage1 = config['message']['close1']
        self.url = config['websocket_url']
        self.tenant_id = config['tenant_id']
        self.timer = None
        self.ser = self.setup_serial()
        self.requiredScan = config['requiredScan']
        self.scan_max_timeout = config['scan_max_time']
        self.scan_method = None

    def send_control_data(self, message):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
            try:
                client_socket.connect((self.server_ip, self.server_port))
                client_socket.sendall(message.encode('utf-8'))
                logger.info(f"Sent data: {message}")
            except Exception as e:
                logger.error(f"Failed to send data: {e}")

    def setup_serial(self):
        """根据配置文件设置串口参数"""
        ser = serial.Serial(
            port=self.serialPort,
            baudrate=self.serialBaudRate,
            timeout=self.serialTimeout
        )
        logger.info(f"打开串口: {ser.name}")
        return ser

    def handle_data(self, barcode):
        """处理读取到的数据"""
        if barcode:  # 确保数据不为空
            barcode = barcode.lstrip('\n')
            self.count += 1
            self.codes.append(barcode)
            logger.info(f"读取到二维码: {barcode}")
            if not self.timer and self.count == 1:
                self.start_timer()  # 只有在没有定时器且count为1时启动定时器

    def start_timer(self):
        """启动定时器，两秒后检查count值"""
        self.timer = threading.Timer(self.scan_max_timeout, self._run_check_count)
        self.timer.start()

    def _run_check_count(self):
        asyncio.run(self.check_count())

    async def check_count(self):
        """定时器到期后检查count值，并发送数据"""
        logger.info(f"定时器结束，计数: {self.count}")
        if self.count != self.requiredScan:
            logger.warning(f"读取的二维码个数不为{self.requiredScan}")
            logger.info(f"读取的二维码列表: {self.codes}")
            if self.scan_method == 'manual':
                self.send_control_data(self.closeMessage1)
            # else:
            #     self.send_control_data(self.closeMessage)
            await self.send_to_backend(self.codes)
        else:
            logger.info(f"读取的二维码个数为{self.requiredScan}")
            logger.info(f"读取的二维码列表: {self.codes}")
            if self.scan_method == 'manual':
                self.send_control_data(self.closeMessage1)
            await self.send_to_backend(self.codes)
        self.count = 0  # 重置计数
        self.timer = None  # 重置定时器
        self.codes.clear()  # 清空codes数组

    async def send_to_backend(self, barcode):
        if self.websocket is None or not self.websocket.open:
            self.websocket = await websockets.connect(self.url)
        message = json.dumps({"type": "barcode", "tenant_id": self.tenant_id, "data": barcode})
        await self.websocket.send(message)
        logger.info(f"发送数据完成: {message}")

    async def listen_for_messages(self):
        while True:
            try:
                if self.websocket is None or not self.websocket.open:
                    self.websocket = await websockets.connect(self.url)
                logger.info("WebSocket连接已建立")
                while True:
                    message = await self.websocket.recv()
                    logger.info(f"收到服务器消息: {message}")
                    if message == "连接成功":
                        logger.info("连接成功")
                    else:
                        try:
                            data = json.loads(message)
                            if data['type'] == 'frontControl':
                                logger.info(f"前端传来: {data['data']}")
                                if data['data']['TCP'] == '0':
                                    logger.info("关闭机器")
                                    if data['data']['scanMethod'] == 'manual':
                                        self.send_control_data(self.closeMessage1)
                                    else:
                                        self.send_control_data(self.closeMessage)
                                    self.requiredScan = int(data['data']['requiredInput'])
                                    self.scan_method = data['data']['scanMethod']
                                elif data['data']['TCP'] == '1':
                                    logger.info("开启机器")
                                    self.send_control_data(self.openMessage)
                                    self.requiredScan = int(data['data']['requiredInput'])
                                elif data['data']['TCP'] == '2':
                                    logger.info("关闭机器")
                                    self.send_control_data(self.closeMessage1)
                                else:
                                    logger.info("其他类型，无效")
                            else:
                                logger.warning(f"未知消息类型: {data['type']}")
                        except json.JSONDecodeError as e:
                            logger.error(f"JSON解析错误: {e}, 消息: {message}")
            except (websockets.exceptions.ConnectionClosed, ConnectionRefusedError) as e:
                logger.error(f"连接已关闭或拒绝: {e}")
                self.websocket = None  # 重新设置为None，以便下次重新连接
                await asyncio.sleep(3)  # 等待一段时间后重试
            except Exception as e:
                logger.error(f"WebSocket监听出错: {e}")
                await asyncio.sleep(3)  # 等待一段时间后重试

    def start_websocket_listener(self):
        """启动WebSocket监听"""
        try:
            asyncio.run(self.listen_for_messages())
        except Exception as e:
            logger.error(f"WebSocket监听出错1: {e}")

    def read_barcode(self):
        """读取二维码数据"""
        buffer = b''  # 用于存储未解码的字节
        while True:
            try:
                if self.ser.in_waiting > 0:
                    # 一次性读取所有可用数据
                    data = self.ser.read(self.ser.in_waiting)
                    buffer += data
                    # 查找完整的条形码数据
                    while b'\r' in buffer:
                        line, buffer = buffer.split(b'\r', 1)
                        barcode = line.decode('utf-8').rstrip()
                        threading.Thread(target=self.handle_data, args=(barcode,)).start()
            except Exception as e:
                logger.error(f"读取条形码时发生错误: {e}")

    def start_reading(self):
        """启动读取线程"""
        read_thread = threading.Thread(target=self.read_barcode)
        read_thread.daemon = False  # 使读取线程不随主程序退出而立即退出
        read_thread.start()
        logger.info("开始读取二维码...")

    async def run(self):
        """运行主循环"""
        try:
            # 创建一个线程来运行WebSocket监听
            ws_task = asyncio.create_task(self.listen_for_messages())
            logger.info("WebSocket监听任务已启动")
            # 启动读取二维码的线程
            self.start_reading()
            try:
                while True:
                    await asyncio.sleep(0.5)  # 保持主线程活跃
            except KeyboardInterrupt:
                logger.info("程序中断")
        finally:
            # 在这里添加必要的清理代码
            if self.websocket and self.websocket.open:
                await self.websocket.close()
            self.ser.close()  # 关闭串口
            logger.info("串口已关闭")

if __name__ == "__main__":
    with open('ScanTransmissionConfig.yml', 'r') as file:
        config1 = yaml.safe_load(file)
    automation = BarcodeReader(config1)
    open_message = config1['message']['open']
    automation.send_control_data(open_message)
    asyncio.run(automation.run())